package com.cskaoyan.search.service.impl;

import com.cskaoyan.mall.dto.ProductDto;
import com.cskaoyan.search.converter.ProductConverter;
import com.cskaoyan.search.dto.SearchResponse;
import com.cskaoyan.search.entity.ItemDocument;
import com.cskaoyan.search.form.SearchRequest;
import com.cskaoyan.search.repository.ProductRepository;
import com.cskaoyan.search.service.ProductSearchService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author raccoon@littleraccoon.xyz
 * @since 2022/12/12 19:39
 */
@Service
public class ProductSearchServiceImpl implements ProductSearchService {
    @Autowired
    ProductRepository productRepository;
    @Autowired
    ProductConverter productConverter;
    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public SearchResponse search(SearchRequest request) {
        // bool query
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.matchQuery("title", request.getKey())); // must: match
        if (request.getPriceLte() != null || request.getPriceGt() != null) {
            query.filter(QueryBuilders.rangeQuery("price")
                    .gte(request.getPriceGt())
                    .lte(request.getPriceLte()));
        } // filter: price range
        // construct pageable parameters
        PageRequest pageable;
        int page = request.getPage() - 1;
        Integer size = request.getSize();
        if ("1".equals(request.getSort())) {
            pageable = PageRequest.of(page, size, Sort.by(Sort.Order.by("price")));
        } else if ("-1".equals(request.getSort())) {
            pageable = PageRequest.of(page, size, Sort.by(Sort.Order.desc("price")));
        } else {
            pageable = PageRequest.of(page, size);
        }
        // construct highlight parameters
        HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags(" </font>");
        // send a search request by elasticsearchRestTemplate
        SearchHits<ItemDocument> search = elasticsearchRestTemplate.search(new NativeSearchQueryBuilder()
                .withQuery(query)
                .withPageable(pageable)
                .withHighlightBuilder(highlightBuilder)
                .build(), ItemDocument.class);
        List<SearchHit<ItemDocument>> searchHits = search.getSearchHits();
        List<ItemDocument> itemDocuments = new ArrayList<>(searchHits.size());
        searchHits.forEach(hit -> {
            hit.getContent().setTitle(hit.getHighlightField("title").get(0));
            itemDocuments.add(hit.getContent());
        });
        SearchResponse<ProductDto> searchResponse = new SearchResponse<>();
        searchResponse.setTotal(search.getTotalHits());
        searchResponse.setData(productConverter.items2Dto(itemDocuments));
        return searchResponse;
    }

    @Override
    public SearchResponse fuzzySearch(SearchRequest request) {
        SearchResponse<ProductDto> searchResponse = new SearchResponse<>();
        searchResponse.setData(productConverter.items2Dto(productRepository.matchSearch(request.getKey())));
        return searchResponse;
    }
}
